﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace Master
{
    /**
     * Takes care of the traffic lights on T junction and adjust the strategy to the lane directions.
     * 
     * @Creator: V.L. de Jager
     * @Changes made by:
     *      - A.J. Janssen
     */
    class TJunctionStrategy : iTrafficLightStrategy
    {
        /*************
        * VARIABLES *
        *************/
        private Thread strategyThread;
        private TrafficMarshal trafficMarshal = new BasicTrafficMarshal();
        private List<Lane> lanes; private List<int> orientationsList;
        private TrafficLightDetails trafficLightDetails;

        /********************
         * PUBLIC FUNCTIONS *
         ********************/
        /**
         * gets all the trafficlights from all the lanes connected to the T junction and saves the lanes in list of lanes based on orientation.
         * Creates and starts the ThreeLaneTrafficLightStrategy thread.
         */
        public void start(InterSection interSection, TrafficLightDetails trafficLightDetails)
        {
            this.trafficLightDetails = trafficLightDetails;
            Dictionary<Side, List<Road>> roadDictionary = interSection.getRoadDictionary();
            lanes = new List<Lane>(); orientationsList = new List<int>();
            foreach (List<Road> sublist in roadDictionary.Values)
                foreach (Road road in sublist)
                    foreach (Lane lane in road.getLanes())
                        if (lane.hasTrafficLight())
                        {
                            if (!orientationsList.Contains(lane.Orientation))
                                orientationsList.Add(lane.Orientation);
                            lanes.Add(lane);
                        }
            strategyThread = new Thread(handleTraffic);
            strategyThread.Start();
        }

        /**
         * Puts all the trafficlights on red and stops the thread.
         */
        public void stop()
        {
            setStatus(lanes, TrafficLightStatus.RED, 250);
            strategyThread.Abort();
        }

        /**
         * Returns true if T junction strategy can be used on giving intersection.
         * Can only be used on a T juntion with 2 entry-lanes.
         */
        public Boolean canBeUsedOnInterSection(InterSection interSection)
        {
            return interSection.isTJunction();
        }

        /**
        * Returns the currently used trafficMarshal in use by TJunctionStrategy.         
        */
        public TrafficMarshal getTrafficMarshal()
        {
            return trafficMarshal;
        }

        /*********************
         * PRIVATE FUNCTIONS *
         *********************/
        private List<Lane> getCurrentLanes(int currentOrientation)
        {
            Trace.WriteLine("Current Orientation: " + currentOrientation);
            List<Lane> currentLanes = new List<Lane>();
            //get lanes with current orientation
            Map.INSTANCE.Dispatcher.Invoke(new Action(delegate()
            {
                currentLanes = Lane.getLanesWithOrientation(lanes, currentOrientation);
                int oppositeOrientaton = Toolkit.alterOrientationBy(currentOrientation, 180),
                    leftSideOrientation = Toolkit.alterOrientationBy(currentOrientation, 90),
                    rightSideOrientation = Toolkit.alterOrientationBy(currentOrientation, -90);
                Boolean hasLeftLanes = Lane.getLanesWithAllowedDirection(currentLanes, Direction.LEFT).Count != 0,
                        //hasLanesOnOppositeSide = Lane.getLanesWithOrientation(lanes, oppositeOrientaton).Count != 0,
                        hasOnlyRightLanes = Lane.getLanesAllowingOnlyDirection(currentLanes, Direction.RIGHT).Count == currentLanes.Count;
                if (hasLeftLanes) //add alle lanes from left side going ONLY right
                {
                    currentLanes.AddRange(Lane.getLanesAllowingOnlyDirection(Lane.getLanesWithOrientation(lanes, leftSideOrientation), Direction.RIGHT));
                    return;
                }
                if (hasOnlyRightLanes) //add all lanes on right side
                {
                    currentLanes.AddRange(Lane.getLanesWithOrientation(lanes, rightSideOrientation));
                    return;
                }
                //no left lanes and at least 1 lane going forward --> add opposite lanes NOT going LEFT
                currentLanes.AddRange(Lane.getLanesWithoutDirection(Lane.getLanesWithOrientation(lanes, oppositeOrientaton), Direction.LEFT));
            }));
            return currentLanes;
        }

        private void setStatus(List<Lane> currentLanes, TrafficLightStatus newStatus, int sleepTime)
        {
            foreach (Lane lane in currentLanes)
                lane.Dispatcher.Invoke(
                        new Action(delegate() { lane.trafficLight.SetValue(TrafficLight.statusProperty, newStatus); })
                        );
            Thread.Sleep(sleepTime);
        }

        private void handleTraffic()
        {
            try
            {
                orientationsList.Sort();
                orientationsList.Reverse();
                Thread.Sleep(5000);
                while (true)
                {
                    foreach (int orientation in orientationsList)
                    {
                        List<Lane> currentLanes = getCurrentLanes(orientation);
                        setStatus(currentLanes, TrafficLightStatus.GREEN, trafficLightDetails.getGreenTime());
                        setStatus(currentLanes, TrafficLightStatus.ORANGE, trafficLightDetails.getOrangeTime());
                        setStatus(currentLanes, TrafficLightStatus.RED, trafficLightDetails.getRedTime());
                    }
                }
            }
            catch (ThreadAbortException e)
            {
                Trace.WriteLine("Thread was aborted by: " + e.GetType());
                setStatus(lanes, TrafficLightStatus.RED, 0);
                return;
            }
        }

     
    }
}
